package data;

import constants.Colors;
import constants.Races;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;

/**
 * Player Class
 * Holds the information for a player
 *
 * @author Jared Moore
 * @date 10/5/13
 */
public class Player implements Comparable, Serializable {
	private int money;
	private String name;
	private Races race;
	private Colors color;
	private LinkedList<Tile> properties;
	private final int[] resources;
	private MULE mule;
	public static final HashMap<Colors, Color> COLORS = new HashMap<>();
	public static final HashMap<Colors, String> COLOR_NAMES = new HashMap<>();
	public static final HashMap<Races, String> RACE_NAMES = new HashMap<>();
	public transient BufferedImage icon;

	/**
	 * Sets up the HashMaps so that the have the correct values
	 */
	static {
		COLORS.put(Colors.BLUE, Color.BLUE);
		COLORS.put(Colors.GREEN, Color.GREEN);
		COLORS.put(Colors.RED, Color.RED);
		COLORS.put(Colors.YELLOW, Color.YELLOW);
		COLORS.put(Colors.ORANGE, Color.ORANGE);
		COLORS.put(Colors.PINK, Color.PINK);

		COLOR_NAMES.put(Colors.BLUE, "blue");
		COLOR_NAMES.put(Colors.GREEN, "green");
		COLOR_NAMES.put(Colors.RED, "red");
		COLOR_NAMES.put(Colors.YELLOW, "yellow");
		COLOR_NAMES.put(Colors.ORANGE, "orange");
		COLOR_NAMES.put(Colors.PINK, "pink");

		RACE_NAMES.put(Races.BONZOID, "Bonzoid");
		RACE_NAMES.put(Races.BUZZITE, "Buzzite");
		RACE_NAMES.put(Races.FLAPPER, "Flapper");
		RACE_NAMES.put(Races.HUMAN, "Human");
		RACE_NAMES.put(Races.UGAITE, "Ugaite");
	}

	/**
	 * Constructor for Player
	 *
	 * @param name      The name of the player
	 * @param race      The race of the player
	 * @param color     The color for the player
	 * @param resources The resources that the player should start with
	 */
	public Player(String name, Races race, Colors color, int[] resources) {
		this.name = name;
		this.race = race;
		this.color = color;
		properties = new LinkedList<>();
		this.resources = resources;

		switch (race) {
			case HUMAN:
				money = 600;
				break;
			case FLAPPER:
				money = 1600;
				break;
			default:
				money = 1000;
				break;
		}

		setIcon();
	}

	public void setIcon() {
		String iconString = "res/races/" + COLOR_NAMES.get(color) + "/" + RACE_NAMES.get(race) + ".png";
		try {
			icon = ImageIO.read(new File(iconString));
		} catch (IOException e) {
			System.err.printf("Player %s image not found", RACE_NAMES.get(race));
			System.exit(1);
		}
	}

	/**
	 * Draws the player on a tile
	 *
	 * @param g            The graphics object to do the drawing
	 * @param tileWidth    The width of the tile
	 * @param tileHeight   The height of the tile
	 * @param tileLocation The location of the tile
	 */
	public void draw(Graphics g, int tileWidth, int tileHeight, Point tileLocation) {
		g.drawImage(icon, tileLocation.x * tileWidth, tileLocation.y * tileHeight, tileWidth, tileHeight, null);
	}

	/**
	 * Adds a property to the player's list of properties
	 *
	 * @param property The property (Tile) to add
	 */
	public void addProperty(Tile property) {
		properties.add(property);
		property.setOwner(this);
	}

	/**
	 * Decrements the food of the player
	 *
	 * @param amount The amount of food to take away
	 * @return True iff the player has > than the amount of food needed
	 */
	public boolean decrementFood(int amount) {
		if (amount > resources[0]) {
			//resources[0] -= amount;
			return false;
		} else {
			resources[0] -= amount;
			return true;
		}
	}

	/**
	 * Decrements the energy of the player
	 *
	 * @param amount The amount of energy to take away
	 * @return True iff the player has > than the amount of energy needed
	 */
	public boolean decrementEnergy(int amount) {
		if (amount > resources[2]) {
			return false;
		} else {
			resources[2] -= amount;
			return true;
		}
	}

	/**
	 * Decrements the crystite of the player
	 *
	 * @param amount The amount of crystite to take away
	 * @return True iff the player has > than the amount of energy needed
	 */
	public boolean decrementCrystite(int amount) {
		if (amount > resources[3]) {
			return false;
		} else {
			resources[3] -= amount;
			return true;
		}
	}

	/**
	 * Decrements the ore of the player
	 *
	 * @param amount The amount of ore to take away
	 * @return True iff the player has > than the ore of energy needed
	 */
	public boolean decrementOre(int amount) {
		if (amount > resources[1]) {
			return false;
		} else {
			resources[1] -= amount;
			return true;
		}
	}

	/**
	 * Adds food to the player
	 *
	 * @param amount The amount to add
	 */
	public void addFood(int amount) {
		resources[0] += amount;
	}

	/**
	 * Adds energy to the player
	 *
	 * @param amount The amount to add
	 */
	public void addEnergy(int amount) {
		resources[2] += amount;
	}

	/**
	 * Adds crystite to the player
	 *
	 * @param amount The amount to add
	 */
	public void addCrystite(int amount) {
		resources[3] += amount;
	}

	/**
	 * Adds ore to the player
	 *
	 * @param amount The amount to add
	 */
	public void addOre(int amount) {
		resources[1] += amount;
	}

	/**
	 * Getter for money
	 *
	 * @return The amount of money the player has
	 */
	public int getMoney() {
		return money;
	}

	/**
	 * Makes a purchase
	 *
	 * @param cost The cost of the item
	 * @return True iff the player has enough money
	 */
	public boolean makePurchase(int cost) {
		if (cost > money) {
			return false;
		} else {
			money -= cost;
			return true;
		}
	}

	/**
	 * Getter for name
	 *
	 * @return The player's name
	 */
	public String getName() {
		return name;
	}

	/**
	 * Getter for color
	 *
	 * @return The java.awt.Color of the player
	 */
	public Color getColor() {
		return COLORS.get(color);
	}

	/**
	 * Getter for color
	 *
	 * @return The constants.Colors of the player
	 */
	public Colors getColors() {
		return color;
	}

	/**
	 * Gets the text of the color of the player
	 *
	 * @return A string in all lowercase of the player's color
	 */
	public String getColorText() {
		return COLOR_NAMES.get(color);
	}

	/**
	 * Getter for the player's properties
	 *
	 * @return A linked list of the player's properties
	 */
	public LinkedList<Tile> getProperties() {
		return properties;
	}

	/**
	 * adds a free mule that the player stores
	 *
	 * @param mule the mule to be stored
	 */
	public void addFreeMule(MULE mule) {
		this.mule = mule;
		try {
			icon = ImageIO.read(new File("res/MULES/" + COLOR_NAMES.get(color) + "/" + MULE.MULE_TYPES_NAMES.
					get(mule.getOutfit()) + "M.U.L.E.png"));
		} catch (IOException e) {
			System.err.printf("%s Mule image is missing", COLOR_NAMES.get(color));
			System.exit(1);
		}
	}

	/**
	 * Whether or not the player has a free MULE
	 *
	 * @return True if the player has a free MULE
	 */
	public boolean hasFreeMule() {
		return this.mule != null;
	}

	/**
	 * Getter for mule
	 *
	 * @return The MULE
	 */
	public MULE getFreeMule() {
		setIcon();
		return mule;
	}

	/**
	 * Sets mule
	 *
	 * @param m The MULE that will take the value of mule
	 */
	public void setFreeMule(MULE m) {
		mule = m;
	}

	@Override
	public String toString() {
		return String.format("Current Player: %s\n, Resources:\n" +
				" Money: %d, Food: %d, Ore: %d, Energy: %d, Crystite %d, Free Mule: %s",
				name, money, resources[0], resources[1], resources[2],
				resources[3], (hasFreeMule() ? "Yes," : "No,"));
	}


	/**
	 * Gets the amount of food the player has
	 *
	 * @return The amount of food the player has
	 */
	public int getFood() {
		return resources[0];
	}

	/**
	 * Gets the amount of ore a player has
	 *
	 * @return The amount of food a player has
	 */
	public int getOre() {
		return resources[1];
	}

	/**
	 * Gets the amount of energy a player has
	 *
	 * @return The amount of energy a player has
	 */
	public int getEnergy() {
		return resources[2];
	}

	/**
	 * Gets the amount of crystite a player has
	 *
	 * @return The amount of crystite a player has
	 */
	public int getCrystite() {
		return resources[3];
	}

	/**
	 * Gets the player's image
	 *
	 * @return An image of the player
	 */
	public BufferedImage getIcon() {
		return icon;
	}

	/**
	 * Adds money to the player's account
	 *
	 * @param money The amount of money to add
	 */
	public void addMoney(int money) {
		this.money += money;
	}

	/**
	 * Checks to see if the player has a certain amount of money
	 *
	 * @param amount The amount to check for
	 * @return true if player has at least the amount of money else, false
	 */
	public boolean hasMoney(int amount) {
		return amount <= this.money;
	}

	/**
	 * Checks to see if the player has a certain amount of food
	 *
	 * @param amount The amount to check for
	 * @return true if player has at least the amount of food else, false
	 */
	public boolean hasFood(int amount) {
		return amount <= getFood();
	}

	/**
	 * Checks to see if the player has a certain amount of energy
	 *
	 * @param amount The amount to check for
	 * @return true if player has at least the amount of energy else, false
	 */
	public boolean hasEnergy(int amount) {
		return amount <= getEnergy();
	}

	/**
	 * Checks to see if the player has a certain amount of ore
	 *
	 * @param amount The amount to check for
	 * @return true if player has at least the amount of ore else, false
	 */
	public boolean hasOre(int amount) {
		return amount <= getOre();
	}

	/**
	 * Checks to see if the player has a certain amount of crystite
	 *
	 * @param amount The amount to check for
	 * @return true if player has at least the amount of crystite else, false
	 */
	public boolean hasCrystite(int amount) {
		return amount <= getCrystite();
	}

	/**
	 * Compares player objects with respect to their scores
	 *
	 * @param o The object being compared to
	 * @return 1 if this player has more money, -1 if the other does, and 0 if they are the same
	 */
	@Override
	public int compareTo(Object o) {
		if (this.getMoney() > ((Player) o).getMoney()) {
			return 1;
		} else if (this.getMoney() < ((Player) o).getMoney()) {
			return -1;
		} else {
			return 0;
		}
	}

}
